/**
 * The Woosta Table Football Engine
 *
 * License... etc.
 **
 * Input Events
 **
 * Author: Sean Micklethwaite
 **/

#ifndef __inputH__
#define __inputH__

#include "kernel.h"
#include "vector.h"

// SDL Graphics (window creation) imply that we use SDL input
#ifdef __GRAPHICS_SDL__
#	define __INPUT_SDL__
#	include "sdlmisc.h"
#elif defined(_WIN32)
#	define __INPUT_WIN32__
#	include <windows.h>
#endif

namespace WtfEngine
{
	/**** Mouse events ****/
	
	typedef enum
	{
		MOUSE_NULL		=NULL,
#	ifdef __INPUT_SDL__
		MOUSE_LEFT		=SDL_BUTTON_LEFT,
		MOUSE_RIGHT		=SDL_BUTTON_RIGHT,
		MOUSE_MIDDLE	=SDL_BUTTON_MIDDLE,
		MOUSE_SCROLLUP	=SDL_BUTTON_WHEELUP,
		MOUSE_SCROLLDOWN=SDL_BUTTON_WHEELDOWN,
#	elif defined(_WIN32)
		MOUSE_LEFT,
		MOUSE_RIGHT,
		MOUSE_MIDDLE,
		MOUSE_SCROLLUP,
		MOUSE_SCROLLDOWN,
#	endif
		MOUSE_SCROLLLEFT,
		MOUSE_SCROLLRIGHT,
	} tMouseButton;
	
	class MouseMoveEvent : public KeyedEvent<MouseMoveEvent>
	{
	private:
		int	X, Y;
		int	DeltaX, DeltaY;

	public:
		MouseMoveEvent()
			: X(0), Y(0), DeltaX(0), DeltaY(0) {};
		MouseMoveEvent(int x, int y, int dx, int dy)
			: X(x), Y(y), DeltaX(dx), DeltaY(dy) {};

		int GetDeltaY() const {
			return DeltaY;
		};

		int GetDeltaX() const {
			return DeltaX;
		};

		int x() const {
			return X;
		};

		int y() const {
			return Y;
		};

		GC_AUTOSIZE(MouseMoveEvent);
	};

	class MouseDownEvent : public KeyedEvent<MouseDownEvent>
	{
	private:
		int X, Y;
		tMouseButton Btn;
		
	public:
		MouseDownEvent()
			: X(0), Y(0), Btn(MOUSE_NULL) {
		};
		MouseDownEvent(int x, int y, tMouseButton b)
			: X(x), Y(y), Btn(b) {
		};
		
		int x() const {
			return X;
		};
	
		int y() const {
			return Y;
		};
		
		tMouseButton getButton() const{
			return Btn;
		};

		GC_AUTOSIZE(MouseDownEvent);
	};

	class MouseUpEvent : public KeyedEvent<MouseUpEvent>
	{
	private:
		int X, Y;
		tMouseButton Btn;
		
	public:
		MouseUpEvent()
		: X(0), Y(0), Btn(MOUSE_NULL) {
		};
		MouseUpEvent(int x, int y, tMouseButton b)
		: X(x), Y(y), Btn(b) {};
		
		int x() const {
			return X;
		};

		int y() const {
			return Y;
		};
		
		tMouseButton getButton() const{
			return Btn;
		};

		GC_AUTOSIZE(MouseUpEvent);
	};


	struct InputKey
	{
		int		unicode;
		int		flags;
#ifdef __INPUT_SDL__
		static const int KMOD_MASK = KMOD_ALT | KMOD_SHIFT | KMOD_CTRL;
#endif
		static const int INTERNATIONAL_MASK = 0xFF80, UNICODE_MASK = 0x7F;

		enum{
			// Use ASCII device control to represent arrow keys
			UP = 17,
   			DOWN = 18,
	  		LEFT = 19,
	 		RIGHT = 20,
			ESC = 27
		};

		InputKey() {};

#ifdef __INPUT_SDL__
		InputKey(SDL_keysym sym){
			assert( SDL_EnableUNICODE(SDL_QUERY) == SDL_ENABLE );

			this->unicode = sym.unicode;
			this->flags = sym.mod & KMOD_MASK;
		};
#endif

		InputKey(int uniKey)
			: unicode(uniKey), flags(NULL) {
		};
		InputKey(int uniKey, int f)
			: unicode(uniKey), flags(f) {
		};

		bool operator == (const InputKey& k) const {
			return (this->unicode == k.unicode && this->flags == k.flags);
		};

		bool operator < (const InputKey& k) const {
			return (this->unicode < k.unicode ||
				((this->unicode == k.unicode) && (this->flags < k.flags)));
		};
	};

	class KeyEvent : public KeyedEvent<KeyEvent>
	{
	private:
		InputKey	mKey;

	public:
		KeyEvent()
			: mKey() {};
		KeyEvent(const InputKey& key)
			: mKey(key) {};

		const InputKey& getKey() const{
			return mKey;
		};

		GC_AUTOSIZE(KeyEvent);
	};

	class KeyDownEvent : public KeyedEvent<KeyDownEvent>
	{
	private:
		InputKey	mKey;

	public:
		KeyDownEvent(const InputKey& key)
			: mKey(key) {};

		EVENT(KeyDownEvent, KeyedEvent<KeyDownEvent>, (
			mKey == pEv->mKey ? 0 : (mKey < pEv->mKey ? -1 : 1)
		));
		GC_AUTOSIZE(KeyDownEvent);
	};

	class KeyUpEvent : public KeyedEvent<KeyUpEvent>
	{
	private:
		InputKey	mKey;

	public:
		KeyUpEvent(const InputKey& key)
			: mKey(key) {};

		EVENT(KeyUpEvent, KeyedEvent<KeyUpEvent>, (
			mKey == pEv->mKey ? 0 : (mKey < pEv->mKey ? -1 : 1)
		));
		GC_AUTOSIZE(KeyUpEvent);
	};

	class InputHandler : public Task<InputHandler>
	{
#	ifdef __INPUT_WIN32__
		BYTE mzKeyState[256];
		HKL mKeyboardLayout;
		HWND mHWnd;
		iVector2D mMousePos;
#	endif

	public:
		InputHandler();

		void		CaptureAllInput(bool b);
		void		EnterGameMode();
		void		EnterGuiMode();

		iVector2D	GetMousePosition() const;
		bool		IsKeyPressed(const InputKey& key) const;

	protected:
		void Run();
	};
};


#endif
